package com.dahuyou.trigger.http;

import com.dahuyou.types.util.JwtUtils;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import java.util.UUID;

/**
 * 模拟授权服务器
 */
//@RestController
@Controller
@RequestMapping("/authServer")
public class AuthServerController {
    //模拟第三方软件注册之后的数据库存储，用户验证第三方软件的合法性（正常流程下是会有一个注册的过程，生成这些信息后，授权f服务保存这些信息的同时，将这些信息交给三方软件进行管理）
    private static Map<String, String> appMap = new HashMap<String, String>();

    static {
        appMap.put("app_id", "APPID_RABBIT");
        appMap.put("app_secret", "APPSECRET_RABBIT");
        appMap.put("redirect_uri", "http://localhost:8899/client/receiveAuthCode");
        appMap.put("scope", "today history");
    }

    // 存储一次合法的请求ID
    private static Map<String, String> reqidMap = new HashMap<String, String>();

    // 存储临时的授权码信息，正常该值存储在如redis中的nosql，并设置5~10分钟的有效期（文档规定的是10分钟）
    private static Map<String, String> codeMap = new HashMap<String, String>();

    // 授权码与授权范围的关系（后续授权码交换访问令牌access_token时校验使用）
    private static Map<String, String[]> codeScopeMap = new HashMap<String, String[]>();

    // 存储颁发过的token信息
    private static Map<String, String> tokenMap = new HashMap<String, String>();

    // 存储颁发过的token信息对应的scope信息，避免访问到没有授权过的API资源
    private static Map<String, String[]> tokenScopeMap = new HashMap<String, String[]>();

    // 刷新令牌信息
    private static Map<String,String> refreshTokenMap =  new HashMap<String, String>();

    /**
     * 跳转到授权页面
     *
     * @param request
     * @return
     */
    @RequestMapping("/goToAuthPage")
    public String goToAuthPage(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
        String responseType = request.getParameter("response_type");
        String redirectUri = request.getParameter("redirect_uri");
        String appId = request.getParameter("app_id");
        String scope = request.getParameter("scope");

        System.out.println("8081 GET responseType: " + responseType);
        // 验证appid是否合法，该值在注册是会生成
        if (!appMap.get("app_id").equals(appId)) {
            request.setAttribute("error", "appid 不合法");
            return "error";
        }
        // 验证redirect_uri，该值在注册时需要录入，避免钓鱼
        if (!appMap.get("redirect_uri").equals(redirectUri)) {
            request.setAttribute("error", "redirect_uri 不合法，想钓鱼？？？");
            return "error";
        }
        // 验证权限scope，其实就是可以操作哪些接口，该值在注册会让用户选择要操作的范围，进而生成一个scope值
//            if(!checkScope(scope)){
        if (!appMap.get("scope").contains(scope)) {
            //超出注册的权限范围
            request.setAttribute("error", "scope 不合法，请检查注册的权限");
            return "error";
        }
        // 生成页面reqid（验证本次请求的合法性）
        String reqid = String.valueOf(System.currentTimeMillis());
        reqidMap.put(reqid, reqid);//保存该reqid值

        request.setAttribute("reqid", reqid);
        request.setAttribute("response_type", responseType);
        request.setAttribute("redirect_uri", redirectUri);
        request.setAttribute("app_id", appId);

        //跳转到授权页面
//        request.getRequestDispatcher("/approve.jsp").forward(request, response);
        return "approve";
        //至此颁发授权码code的准备工作完毕

    }

    /**
     * 生成授权码
     *
     * @param request
     * @param response
     * @return
     * @throws IOException
     * @throws ServletException
     */
    @RequestMapping("/generateAuthCode")
    public void generateAuthCode(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
        System.out.println("start accept post req, generate access_toen");
        String reqType = request.getParameter("reqType");

        String grantType = request.getParameter("grant_type");
        String appId = request.getParameter("app_id");
        String appSecret = request.getParameter("app_secret");

        String responseType = request.getParameter("response_type");
        String redirectUri = request.getParameter("redirect_uri");
        String scope = request.getParameter("scope");
//处理用户点击approve按钮动作
        if ("approve".equals(reqType)) {
            String reqid = request.getParameter("reqid");//假设一定能够获取到值

            if (!reqidMap.containsKey(reqid)) {
                return;
            }

            if ("code".equals(responseType)) {

                String[] rscope = request.getParameterValues("rscope");

                if (/*!checkScope(rscope)*/false) {//验证权限范围，对又验证一次
                    //超出注册的权限范围
                    System.out.println("out of scope ...");
                    return;
                }
                // 授权码
                String code = generateCode(appId, "USERTEST");//模拟登陆用户为USERTEST
                codeScopeMap.put(code, rscope);//授权范围与授权码做绑定

//                Map<String, String> params = new HashMap<String, String>();
//                params.put("code", code);

//                String toAppUrl = URLParamsUtil.appendParams(redirectUri, params);//构造第三方软件的回调地址，并重定向到该地址
                String toAppUrl = redirectUri + "?code=" + code;
                response.sendRedirect(toAppUrl);//授权码流程的【第二次】重定向
            }
        }
    }

    @RequestMapping("/exchangeAccessTokenByCode")
    @ResponseBody
    public void exchangeAccessTokenByCode(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
        System.out.println("start accept post req, generate access_toen");
        String reqType = request.getParameter("reqType");

        String grantType = request.getParameter("grant_type");
        String appId = request.getParameter("app_id");
        String appSecret = request.getParameter("app_secret");

        String responseType = request.getParameter("response_type");
        String redirectUri = request.getParameter("redirect_uri");
        String scope = request.getParameter("scope");
        //处理授权码流程中的 颁发访问令牌 环节
        if ("authorization_code".equals(grantType)) {
            if (!appMap.get("app_id").equals(appId)) {
                response.getWriter().write("app_id is not available");
                return;
            }

            if (!appMap.get("app_secret").equals(appSecret)) {
                response.getWriter().write("app_secret is not available");
                return;
            }

            String code = request.getParameter("code");

//            if(!isExistCode(code)){//验证code值
            if (false) {// TODO 验证code值
                return;
            }
            codeMap.remove(code);//授权码一旦被使用，须要立即作废

            System.out.println("start generate access_toen");
//            String accessToken = generateAccessToken(appId, "USERTEST");//生成访问令牌access_token的值
            String accessToken = JwtUtils.generateJwt("USERTEST");
            tokenScopeMap.put(accessToken, codeScopeMap.get(code));//授权范围与访问令牌绑定

            String refreshToken = generateRefreshToken(appId, "USERTEST");//生成刷新令牌refresh_token的值


            // TODO: 2020/2/28 将accessToken和refreshToken做绑定 ，将refreshToken和codeScopeMap做绑定

            response.getWriter().write(accessToken + "|" + refreshToken);

        }
    }

    @RequestMapping("/exchangeAccessTokenRefreshToken")
    @ResponseBody
    public void exchangeAccessTokenRefreshToken(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
        System.out.println("start accept post req, generate access_toen");
        String reqType = request.getParameter("reqType");

        String grantType = request.getParameter("grant_type");
        String appId = request.getParameter("app_id");
        String appSecret = request.getParameter("app_secret");

        String responseType = request.getParameter("response_type");
        String redirectUri =request.getParameter("redirect_uri");
        String scope =request.getParameter("scope");

//        if(!"APPIDTEST".equals(appId)){
        if(!"APPID_RABBIT".equals(appId)){
            response.getWriter().write("app_id is not available");
            return;
        }

        if(!"APPSECRETTEST".equals(appSecret)){
            response.getWriter().write("app_secret is not available");
            return;
        }


        String refresh_token = request.getParameter("refresh_token");

        if(!refreshTokenMap.containsKey(refresh_token)){//该refresh_token值不存在
            return;
        }

        String appStr = refreshTokenMap.get(refresh_token);
        if(!appStr.startsWith(appId+"|"+"USERTEST")){//该refresh_token值 不是颁发给该 第三方软件的
            return;
        }

        String accessToken = generateAccessToken(appId,"USERTEST");//生成访问令牌access_token的值

        // TODO: 2020/2/28 删除旧的access_token 、删除旧的refresh_token、生成新的refresh_token

        String refreshToken = generateRefreshToken(appId, "USERTEST");//生成刷新令牌refresh_token的值


        // TODO: 2020/2/28 将accessToken和refreshToken做绑定 ，将refreshToken和codeScopeMap做绑定
        response.setContentType("text/html;charset=utf-8");
        response.getWriter().write("刷新token成功了，新的token信息为：" + accessToken + "|" + refreshToken);

    }

    /**
     * 生成refresh_token值
     * @param appId
     * @param user
     * @return
     */
    private String generateRefreshToken(String appId,String user){

        String refreshToken = UUID.randomUUID().toString();

        refreshTokenMap.put(refreshToken,appId+"|"+user+"|"+System.currentTimeMillis());//在这一篇章我们仅作为演示用，实际这应该是一个全局数据库,并且有有效期

        return refreshToken;
    }


    /**
     * 生成access_token值
     *
     * @param appId
     * @param user
     * @return
     */
    private String generateAccessToken(String appId, String user) {

        String accessToken = UUID.randomUUID().toString();

        String expires_in = "1";//1天时间过期

        tokenMap.put(accessToken, appId + "|" + user + "|" + System.currentTimeMillis() + "|" + expires_in);//在这一篇章我们仅作为演示用，实际这应该是一个全局数据库,并且有有效期

        return accessToken;
    }


    /**
     * 生成code值
     *
     * @return
     */
    private String generateCode(String appId, String user) {
        Random r = new Random();
        StringBuilder strb = new StringBuilder();
        for (int i = 0; i < 8; i++) {
            strb.append(r.nextInt(10));
        }

        String code = strb.toString();
        codeMap.put(code, appId + "|" + user + "|" + System.currentTimeMillis());//在这一篇章我们仅作为演示用，实际这应该是一个全局内存数据库，有效期官方建议是10分钟

        return code;
    }

}